package com.tcs.utils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

import weka.clusterers.DBSCAN;
import weka.core.Instances;
import weka.core.converters.ConverterUtils.DataSource;

import com.tcs.tsrm.beans.Cluster;
import com.tcs.tsrm.beans.ClusteringThresholds;

public class Ranking {

	HashMap<String, Double> eps = new HashMap<String, Double>();
	private static int support=0;
	private static int supMotif=0;
	public void getRankedMotifs(String inputFolderPath ,String outputFolderPath,ClusteringThresholds thresholds)
			throws Exception {
		support = thresholds.getSupport();
		HashMap<String, HashMap<String, double[]>> allMotifs = loadClusters(
				inputFolderPath,1);
		eps = calculateEps(allMotifs);

		HashMap<String, HashMap<String, Double>> averageMotifs = getAverageMotifs(allMotifs);
		HashMap<String, HashMap<Integer, ArrayList<String>>> dbClusters = dbScanClustering(averageMotifs);
		if((support < 5)  && (supMotif <5))
			calculateRank(inputFolderPath,outputFolderPath);
		else
			calculateRank(dbClusters, allMotifs, averageMotifs, outputFolderPath);

	}

	private void calculateRank(
			HashMap<String, HashMap<Integer, ArrayList<String>>> dbClusters,
			HashMap<String, HashMap<String, double[]>> allMotifs,
			HashMap<String, HashMap<String, Double>> averageMotifs,
			String outputFolderPath) throws IOException {
		Set<String> motifIdSet = dbClusters.keySet();
		for (String motifID : motifIdSet) {
			HashMap<String, double[]> eachOrgSubs = allMotifs.get(motifID);
			HashMap<String, Double> eachMotifAvg = averageMotifs.get(motifID);
			HashMap<Integer, ArrayList<String>> eachDbCluster = dbClusters
					.get(motifID);
			Set<Integer> clusterIdSet = eachDbCluster.keySet();
			for (Integer cid : clusterIdSet) {
				ArrayList<double[]> points = new ArrayList<double[]>();
				ArrayList<Double> pointsAvg = new ArrayList<Double>();
				ArrayList<String> subseqIds = eachDbCluster.get(cid);
				if (subseqIds.size() > support) {
					for (String sids : subseqIds) {
						points.add(eachOrgSubs.get(sids));
						pointsAvg.add(eachMotifAvg.get(sids));
					}
					double Dev = calculateDeviation(points);
					double Level = calculateLevel(pointsAvg);
					double rank = (0.3 * Dev) + (0.3 * Level)
							+ (0.4 * subseqIds.size());
					printRankedMotifs(outputFolderPath, rank, subseqIds,
							points, motifID);
				} else {
					continue;
				}
			}
		}
	}

	private void calculateRank(
			String folderName,
			String outputFolderPath) throws IOException {
		BufferedReader br=null;
		HashMap<Integer, double[]> originalCluster = Cluster.originalMatrixWithoutNormalisationHashMap;
		String fileNameWPath = "";
		File docDir = new File(folderName);
		double count=0;
		try {
			File[] files = docDir.listFiles();
			for (File f : files) {
				ArrayList<String> subseqIds = new ArrayList<String>();
				ArrayList<double[]> points = new ArrayList<double[]>();
				if (f.isDirectory() || f.isHidden() || !f.exists()
						|| !f.canRead()) {
					continue;
				}

				fileNameWPath = f.getCanonicalPath();
				String fileName = f.getName();
				if (!fileName.endsWith(".txt")) {
					continue;
				}
				br = new BufferedReader(new FileReader(fileNameWPath));
				String line;
				String[] stPoint;
				String temporaryVariable;
				double[] stPoint2;

				int temp=0;
				while ((line = br.readLine()) != null) {
					stPoint = line.split(",");
					temp=Integer.parseInt(stPoint[0]);
					stPoint2 = originalCluster.get(temp);
					temporaryVariable=stPoint[0];
					subseqIds.add(temporaryVariable);
					points.add(stPoint2);
				}
				
				printRankedMotifs(outputFolderPath,count++, subseqIds,
						points,String.valueOf(count));

			}
			
			br.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}


	}



	private void printRankedMotifs(String outputFolderPath, double rank,
			ArrayList<String> subseqIds, ArrayList<double[]> points,
			String motifID) throws IOException {
		File fileOriginal;
		DecimalFormat df = new DecimalFormat("0.00");
		String rankString = df.format(rank);
		fileOriginal = new File(outputFolderPath + "/" + motifID + "-"
				+ rankString + ".csv");
		fileOriginal.getParentFile().mkdirs();
		FileWriter csvOriginalStream = new FileWriter(fileOriginal);
		BufferedWriter csvOriginalOut = new BufferedWriter(csvOriginalStream);
		for (int i = 0; i < subseqIds.size(); i++) {
			csvOriginalOut.write(subseqIds.get(i)
					+ getPointString(points.get(i)));
			csvOriginalOut.write("\n");
		}

		csvOriginalOut.close();
		csvOriginalStream.close();
	}

	private String getPointString(double[] points) {
		String pointString = "";
		for (double d : points) {
			pointString += "," + d;
		}
		return pointString;
	}

	private static double calculateLevel(ArrayList<Double> pointsAvg) {
		double sum = 0.0;
		for (double avg : pointsAvg) {
			sum += avg;
		}
		return sum / pointsAvg.size();
	}

	private static double calculateDeviation(ArrayList<double[]> points) {

		int size = points.size();
		double dev = 0.0;
		for (double[] eachPoint : points) {
			dev += getMaxMinusMin(eachPoint);
		}
		return dev / size;
	}

	private static double getMaxMinusMin(double[] eachPoint) {
		double min = Double.MAX_VALUE;
		double max = Double.MIN_VALUE;
		for (double p : eachPoint) {
			if (p < min) {
				min = p;
			}
			if (p > max) {
				max = p;
			}
		}
		return max - min;
	}

	private static HashMap<String, Double> calculateEps(
			HashMap<String, HashMap<String, double[]>> allMotifs) {
		HashMap<String, Double> eps = new HashMap<String, Double>();
		Set<String> motifIdSet = allMotifs.keySet();
		for (String motifID : motifIdSet) {
			double min = Double.MAX_VALUE;
			double max = Double.MIN_VALUE;
			HashMap<String, double[]> eachMotif = allMotifs.get(motifID);
			int support = eachMotif.size();
			Set<String> subsIDset = eachMotif.keySet();
			for (String eachSubs : subsIDset) {
				for (double coOrdinate : eachMotif.get(eachSubs)) {
					if (coOrdinate < min) {
						min = coOrdinate;
					}
					if (coOrdinate > max) {
						max = coOrdinate;
					}
				}
			}
			double epsilon = support / (max - min);
			supMotif=support;
			eps.put(motifID, epsilon);
		}

		return eps;
	}

	private HashMap<String, HashMap<Integer, ArrayList<String>>> dbScanClustering(
			HashMap<String, HashMap<String, Double>> averageMotifs)
					throws Exception {
		HashMap<String, HashMap<Integer, ArrayList<String>>> dbClusters = new HashMap<String, HashMap<Integer, ArrayList<String>>>();
		Set<String> motifIdSet = averageMotifs.keySet();
		for (String motifID : motifIdSet) {
			HashMap<String, Double> eachMotif = averageMotifs.get(motifID);
			ArrayList<Double> avgEachMotif = getAverages(eachMotif);
			HashMap<Integer, String> lineToSubs = getLineToSubsMap(averageMotifs
					.get(motifID));
			writeFileArrf(avgEachMotif);
			DBSCAN db = new DBSCAN();
			db.setMinPoints(3);
			Instances data = DataSource.read("data.arff");
			double epsilon = eps.get(motifID) / 1;
			db.setEpsilon(epsilon);
			db.buildClusterer(data);
			HashMap<Integer, ArrayList<String>> clusters = new HashMap<Integer, ArrayList<String>>();
			ArrayList<String> subsIndex = null;
			for (int i = 0; i < data.numInstances(); i++) {
				try {

					int key = db.clusterInstance(data.instance(i));
					subsIndex = clusters.get(key);
					if (subsIndex == null) {
						subsIndex = new ArrayList<String>();
						subsIndex.add(lineToSubs.get(i));
					} else {
						subsIndex.add(lineToSubs.get(i));

					}
					clusters.put(key, subsIndex);
				} catch (Exception e) {
					continue;
				}
			}
			dbClusters.put(motifID, clusters);
		}
		return dbClusters;
	}

	private HashMap<Integer, String> getLineToSubsMap(
			HashMap<String, Double> motif) {
		HashMap<Integer, String> lToS = new HashMap<Integer, String>();
		Set<String> subsIDset = motif.keySet();
		int count = 0;
		for (String eachSubs : subsIDset) {
			lToS.put(count, eachSubs);
			count++;
		}
		return lToS;
	}

	private static ArrayList<Double> getAverages(HashMap<String, Double> motif) {
		ArrayList<Double> averageList = new ArrayList<Double>();
		Set<String> subsIDset = motif.keySet();
		for (String eachSubs : subsIDset) {
			averageList.add(motif.get(eachSubs));
		}
		return averageList;
	}

	private static void writeFileArrf(ArrayList<Double> avgMotif)
			throws IOException {
		PrintWriter statOut2 = new PrintWriter(new BufferedWriter(
				new FileWriter("data.arff", false)));
		statOut2.write("@RELATION avgMotif" + "\n" + "\n");
		statOut2.write("@ATTRIBUTE data NUMERIC" + "\n" + "\n");
		statOut2.write("@DATA" + "\n");
		for (double average : avgMotif) {
			statOut2.write("\n");
			statOut2.write("" + average);
		}
		statOut2.close();
	}

	private static HashMap<String, HashMap<String, Double>> getAverageMotifs(
			HashMap<String, HashMap<String, double[]>> allMotifs) {

		HashMap<String, HashMap<String, Double>> averageAllMotifs = new HashMap<String, HashMap<String, Double>>();
		Set<String> motifIdSet = allMotifs.keySet();
		for (String motifID : motifIdSet) {
			HashMap<String, Double> avgEachMotif = new HashMap<String, Double>();
			HashMap<String, double[]> eachMotif = allMotifs.get(motifID);
			Set<String> subsIDset = eachMotif.keySet();
			for (String eachSubs : subsIDset) {
				double[] point = eachMotif.get(eachSubs);
				avgEachMotif.put(eachSubs, calculateAverage(point));
			}
			averageAllMotifs.put(motifID, avgEachMotif);
		}
		return averageAllMotifs;
	}

	private static Double calculateAverage(double[] point) {

		double sum = 0.0;
		for (double coOrdinate : point) {
			sum += coOrdinate;
		}
		return (sum / point.length);
	}

	public HashMap<String, HashMap<String, double[]>> loadClusters(
			String inputFolder,int pointsToSkip) {
		String fileNameWPath = "";
		File docDir = new File(inputFolder);
		String clusterID = "";
		HashMap<String, double[]> aCluster = new HashMap<String, double[]>();
		HashMap<String, HashMap<String, double[]>> allClustersMap = new HashMap<String, HashMap<String, double[]>>();

		try {
			File[] files = docDir.listFiles();
			for (File f : files) {
				if (f.isDirectory() || f.isHidden() || !f.exists()
						|| !f.canRead()) {
					continue;
				}

				fileNameWPath = f.getCanonicalPath();
				String fileName = f.getName();

				if (!fileName.endsWith(".txt")) {
					continue;
				}

				aCluster = loadCluster(fileNameWPath,pointsToSkip);
				clusterID = fileName.substring(0, fileName.length() - 4);
				allClustersMap.put(clusterID, aCluster);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return allClustersMap;
	}

	public static HashMap<String, double[]> loadCluster(String fileName,
			int pointsToSkip) {

		BufferedReader br;
		HashMap<String, double[]> thisCluster = new HashMap<String, double[]>();
		HashMap<Integer, double[]> originalCluster = Cluster.originalMatrixWithoutNormalisationHashMap;
		try {
			br = new BufferedReader(new FileReader(fileName));
			String line;
			String[] stPoint;
			double[] stPoint2;
			double[] aPoint;
			int temp=0;
			while ((line = br.readLine()) != null) {
				stPoint = line.split(",");
				temp=Integer.parseInt(stPoint[0]);
				stPoint2 = originalCluster.get(temp);
				aPoint = convertArray(stPoint2, pointsToSkip);
				thisCluster.put(stPoint[0], aPoint);
			}
			br.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return thisCluster;
	}

	public static double[] convertArray(double[] stPoint, int pointsToSkip) {
		double[] aPoint = new double[stPoint.length - pointsToSkip];
		for (int i = pointsToSkip; i < stPoint.length; i++) {
			aPoint[i - 1] = (stPoint[i]);
		}

		return aPoint;
	}

	public static ArrayList<Double> convertToArrayList(double[] stPoint) {
		ArrayList<Double> motifID = new ArrayList<Double>();
		for (int i = 0; i < stPoint.length; i++) {

			motifID.add(stPoint[i]);
		}

		return motifID;
	}

}
